package com.wzh.ojCommon.util;


import com.alibaba.nacos.shaded.com.google.gson.Gson;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.wzh.ojCommon.exception.BusinessException;
import org.springframework.data.redis.core.*;

import java.io.IOException;
import java.io.Serializable;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.TimeUnit;

import static com.wzh.ojCommon.common.ErrorCode.SYSTEM_ERROR;


/**
 * @author chenfang
 */
public class RedisUtils {
    private volatile static RedisTemplate redisTemplate;

    private volatile static StringRedisTemplate stringRedisTemplate;

    private static final Gson GSON=new Gson();

    public synchronized static RedisTemplate getRedisTemplate() {
        if (redisTemplate == null) {
            redisTemplate = SpringUtils.getBean("redisTemplate", RedisTemplate.class);
        }
        return redisTemplate;
    }

    public synchronized static StringRedisTemplate getStringRedisTemplate() {
        if (stringRedisTemplate == null) {
            stringRedisTemplate = SpringUtils.getBean("stringRedisTemplate", StringRedisTemplate.class);
        }
        return stringRedisTemplate;
    }

    /**
     * 写入缓存
     *
     * @param key
     * @param value
     * @return
     */
    @SuppressWarnings("unchecked")
    public static void set(final String key, Object value) {
        ValueOperations<Serializable, Object> operations = getRedisTemplate().opsForValue();
        if (value instanceof String) {
            operations.set(key, value);
        } else {
            operations.set(key, GSON.toJson(value));
        }
    }

    /**
     * 写入缓存设置时效时间
     *
     * @param key
     * @param value
     * @return
     */
    @SuppressWarnings("unchecked")
    public static void set(final String key, Object value, Long expireTime) {
        ValueOperations<Serializable, Object> operations = getRedisTemplate().opsForValue();
        if (value instanceof String) {
            operations.set(key, value);
        } else {
            operations.set(key,  GSON.toJson(value));
        }
        redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 写入缓存设置时效时间
     *
     * @param key
     * @param value
     * @return
     */
    @SuppressWarnings("unchecked")
    public static void set(final byte[] key, int expireTime, byte[] value) {
        ValueOperations<Serializable, Object> operations = getRedisTemplate().opsForValue();
        operations.set(key, value);
        redisTemplate.expire(key, expireTime, TimeUnit.SECONDS);
    }

    /**
     * 批量删除对应的value
     *
     * @param keys
     */
    public static void remove(final String... keys) {
        for (String key : keys) {
            remove(key);
        }
    }

    /**
     * 批量删除对应的value
     *
     * @param keys
     */
    public static void remove(final byte[]... keys) {
        for (byte[] key : keys) {
            remove(key);
        }
    }

    /**
     * 批量删除key
     *
     * @param pattern
     */
    @SuppressWarnings("unchecked")
    public static void removePattern(final String pattern) {
        Set<Serializable> keys = getRedisTemplate().keys(pattern);
        if (keys.size() > 0) {
            getRedisTemplate().delete(keys);
        }
    }

    /**
     * 删除对应的value
     *
     * @param key
     */
    @SuppressWarnings("unchecked")
    public static void remove(final String key) {
        if (exists(key)) {
            getRedisTemplate().delete(key);
        }
    }

    /**
     * 删除对应的value
     *
     * @param key
     */
    @SuppressWarnings("unchecked")
    public static void remove(final byte[] key) {
        if (exists(key)) {
            getRedisTemplate().delete(key);
        }
    }

    /**
     * 判断缓存中是否有对应的value
     *
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static boolean exists(final String key) {
        return getRedisTemplate().hasKey(key);
    }

    /***
     * 判断缓存中是否有对应的value
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static boolean exists(final byte[] key) {
        return getRedisTemplate().hasKey(key);
    }

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T get(final String key, Class<T> clazz) {
        ValueOperations<Serializable, Object> operations = getRedisTemplate().opsForValue();
        if (isBasicData(clazz)) {
            return (T) operations.get(key);
        }
        return (T) JsonMapper.fromJsonString((String) operations.get(key), clazz);
    }

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static <T> T get(final String key, Class<T> clazz, Class<?>... elementClasses) {
        ValueOperations<Serializable, Object> operations = getRedisTemplate().opsForValue();
        if (isBasicData(clazz)) {
            return (T) operations.get(key);
        }
        return (T) JsonMapper.fromJsonString((String) operations.get(key), clazz, elementClasses);
    }

    /**
     * 读取缓存
     *
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static byte[] get(final byte[] key) {
        byte[] result = null;
        ValueOperations<Serializable, Object> operations = getRedisTemplate().opsForValue();
        result = (byte[]) operations.get(key);
        return result;
    }

    /**
     * 哈希 添加
     *
     * @param key
     * @param hashKey
     * @param value
     */
    @SuppressWarnings("unchecked")
    public static void hmSet(String key, Object hashKey, Object value) {
        HashOperations<String, Object, Object> hash = getRedisTemplate().opsForHash();
        hash.put(key, hashKey, value);
    }

    /**
     * 哈希获取数据
     *
     * @param key
     * @param hashKey
     * @return
     */
    @SuppressWarnings("unchecked")
    public static Object hmGet(String key, Object hashKey) {
        HashOperations<String, Object, Object> hash = getRedisTemplate().opsForHash();
        return hash.get(key, hashKey);
    }

    /**
     * 列表添加(left)
     *
     * @param k
     * @param v
     */
    @SuppressWarnings("unchecked")
    public static void lPush(String k, Object v) {
        ListOperations<String, Object> list = getRedisTemplate().opsForList();
        if (isBasicData(v.getClass())) {
            list.leftPush(k, v);
        } else {
            list.leftPush(k, JsonMapper.toJsonString(v));
        }


    }

    /**
     * 列表添加
     *
     * @param k
     * @param v
     */
    @SuppressWarnings("unchecked")
    public static void rPush(String k, Object v) {
        ListOperations<String, Object> list = getRedisTemplate().opsForList();
        if (isBasicData(v.getClass())) {
            list.rightPush(k, v);
        } else {
            list.rightPush(k,  GSON.toJson(v));
        }

    }

    /***
     * 根据索引位置获取元素
     * @param k
     * @param index
     * @return
     */
    @SuppressWarnings("unchecked")
    public static Object lIndex(String k, int index) {
        ListOperations<String, Object> list = getRedisTemplate().opsForList();
        return list.index(k, index);

    }

    /**
     * 获取列表元素(right)
     *
     * @param k
     * @param clazz
     */
    @SuppressWarnings("unchecked")
    public static <T> T rPop(String k, Class<T> clazz) {
        ListOperations<String, Object> list = getRedisTemplate().opsForList();
        if (isBasicData(clazz)) {
            return (T) list.rightPop(k);
        }
        return JsonMapper.fromJsonString((String) list.rightPop(k), clazz);

    }

    /**
     * 获取列表元素(left)
     *
     * @param k
     * @param clazz
     */
    @SuppressWarnings("unchecked")
    public static <T> T lPop(String k, Class<T> clazz) {
        ListOperations<String, Object> list = getRedisTemplate().opsForList();
        if (isBasicData(clazz)) {
            return (T) list.leftPop(k);
        }
        return JsonMapper.fromJsonString((String) list.leftPop(k), clazz);

    }

    /**
     * 列表获取
     * 返回列表中指定区间内的元素，区间以偏移量 START 和 END 指定。 其中 0 表示列表的第一个元素， 1 表示列表的第二个元素，以此类推。
     * 你也可以使用负数下标，以 -1 表示列表的最后一个元素， -2 表示列表的倒数第二个元素，以此类推。
     *
     * @return
     */
    @SuppressWarnings("unchecked")
    public static List<Object> listRange(String k, long l, long l1) {
        ListOperations<String, Object> list = getRedisTemplate().opsForList();
        return list.range(k, l, l1);
    }

    /***
     * 获取列表长度
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static long getListSize(String key) {
        ListOperations<String, Object> list = getRedisTemplate().opsForList();
        return list.size(key);
    }

    /**
     * 集合添加
     *
     * @param key
     * @param value
     */
    @SuppressWarnings("unchecked")
    public static void add(String key, Object value) {
        SetOperations<String, Object> set = getRedisTemplate().opsForSet();
        set.add(key, value);
    }

    /**
     * 集合获取
     *
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static Set<Object> setMembers(String key) {
        SetOperations<String, Object> set = getRedisTemplate().opsForSet();
        return set.members(key);
    }

    /**
     * 有序集合添加
     *
     * @param key
     * @param value
     * @param scoure
     */
    @SuppressWarnings("unchecked")
    public static void zAdd(String key, Object value, double scoure) {
        ZSetOperations<String, Object> zset = getRedisTemplate().opsForZSet();
        zset.add(key, value, scoure);
    }

    /**
     * 有序集合获取
     *
     * @param key
     * @param scoure
     * @param scoure1
     * @return
     */
    @SuppressWarnings("unchecked")
    public static Set<Object> rangeByScore(String key, double scoure, double scoure1) {
        ZSetOperations<String, Object> zset = getRedisTemplate().opsForZSet();
        return zset.rangeByScore(key, scoure, scoure1);
    }

    /***
     * 模糊匹配所有key
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static Set<String> keys(String key) {
        Set<String> set = getRedisTemplate().keys(key);
        if (set == null) {
            set = new HashSet<>();
        }
        return set;
    }

    /***
     *  获取key剩余存活时间
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static long getExpire(byte[] key) {
        if (exists(key)) {
            return getRedisTemplate().getExpire(key);
        }
        return -1;
    }

    /***
     *  获取key剩余存活时间
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static long getExpire(String key) {
        if (exists(key)) {
            return getRedisTemplate().getExpire(key);
        }
        return -1;
    }

    /***
     * 获取所有键
     * @param key
     * @return
     */
    @SuppressWarnings("unchecked")
    public static Set<String> getAllKey(String key) {
        return getRedisTemplate().keys(key);
    }

    @SuppressWarnings("unchecked")
    public static void del(String key) {
        getRedisTemplate().delete(key);
    }

    private static boolean isBasicData(Class className) {
        return (className.equals(Integer.class) ||
                className.equals(Byte.class) ||
                className.equals(Long.class) ||
                className.equals(Double.class) ||
                className.equals(Float.class) ||
                className.equals(Character.class) ||
                className.equals(Short.class) ||
                className.equals(Boolean.class) ||
                className.equals(String.class) ||
                className.equals(Object.class));
    }

    private static final ObjectMapper mapper = new ObjectMapper();


    /**
     * 存入hash缓存
     *
     * @param key
     * @param t
     */
    public static void setHash(String key, Object t) {
        try {
            boolean isKey = RedisUtils.exists(key);
            String strObject = mapper.writeValueAsString(t);
            if (isKey) {
                RedisUtils.remove(key);
                getStringRedisTemplate().opsForValue().set(key, strObject);
            } else {
                getStringRedisTemplate().opsForValue().set(key, strObject);
            }
        } catch (IOException e) {
            throw new BusinessException(e.getMessage(),SYSTEM_ERROR.getCode());
        }
    }


    /**
     * 删除hash缓存
     *
     * @param key
     */
    public static void delHash(String key) {
        boolean isKey = RedisUtils.exists(key);
        if (isKey) {
            RedisUtils.remove(key);
        }
    }

    /**
     * 获取hash缓存
     *
     * @param key
     * @param t
     * @return
     */
    public static Object getHash(String key, Object t) {
        boolean isKey = RedisUtils.exists(key);
        if (isKey) {
            String strObject = getStringRedisTemplate().opsForValue().get(key);
            try {
                return mapper.readValue(strObject, t.getClass());
            } catch (IOException e) {
                throw new BusinessException(e.getMessage(),SYSTEM_ERROR.getCode());
            }
        }
        return null;
    }
}